/*
 * Copyright (c) 2011 Seoul National University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Jung Ho Ahn  ( gajh@snu.ac.kr )
 *
 */

#ifndef __KMERHASHTABLE_H__
#define __KMERHASHTABLE_H__

#include <cstdint>
#include <fstream>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include "ContigNode.h"
#include "InputOptions.h"
#include "Kmer.h"
#include "KmerNode.h"
#include "Reader.h"
#include "SequenceBuffer.h"
#include "BlockAllocator.h"

#include <unordered_map>
#include <unordered_set>
using namespace __gnu_cxx;

using namespace std;

const uint64_t cacheLineSize = 8;  // unit = words

struct Nodes
{
  KmerNode * kmerNode;
  KmerNode * kmerNodeRC;
};


union CountOrNodes
{
  uint64_t count;
  //Nodes    nodes;
  KmerNode * nodes;  // kmerNode and kmerNodeRC are allocated consecutively

  CountOrNodes(uint64_t count_ = 1) : count(count_) { }
};

typedef pair<Kmer *, CountOrNodes> KmerPair;
typedef pair<Kmer, uint64_t> KmerPairTmp;
//typedef map<Kmer *, CountOrNodes, KmerLt> KmerHash;
//typedef set<ContigNode *> ContigHash;
typedef unordered_map<Kmer *, CountOrNodes, KmerHashConfiguration, KmerEqual> KmerHash;
typedef unordered_map<Kmer, uint64_t, KmerHashConfiguration, KmerEqual, MyCustomAlloc< pair<Kmer, uint64_t> > > KmerHashTmp;
typedef unordered_set<ContigNode *> ContigHash;

class kmer_thread_args;

class KmerCoverageTable
{
  public:
    KmerCoverageTable(InputOptions * options_);
    ~KmerCoverageTable();

    KmerNode * find(Kmer * kmer) const;
    inline uint32_t getKmersize()  const { return kmersize; }
    uint64_t getTableSize() const;
    uint64_t getKmerNumber() const;
    uint64_t getTmpTableSize() const;

    void processReads();
    void populateKmerNodes();
    void linkKmers();
    void initializeContigs();
    void displayNodes();
    void displayKmerCountHistogram();
    void displayContigSizeHistogram();
    void runInteractiveMode();

  private:
    InputOptions * options;
    uint32_t   kmersize;
    uint64_t   kcount;
    uint64_t   ccount;
    ContigHash * standaloneContigs;
    ContigHash * activeContigs;
    map<uint64_t, uint64_t> histogram;
    map<uint64_t, uint64_t> contigSizeHistogram;
    uint64_t   kmerBatchSize;
    int32_t    numHashmaps;
    int32_t    numThreads;

    bool       createContigSizeHistogram;
    bool       display_repeats;
    bool       per_file_error_removal;
    bool       per_size_error_removal;
    bool       first_error_removal;
    uint64_t   error_removal_block_size;
    uint64_t   error_removal_block_size_first;
    bool       parallelize_kmer_linking;
    bool       parallelize_kmer_node_population;
    bool       parallelize_read_processing;
    bool       parallelize_contig_initialization;
    bool       tight_memory_management;
    bool       display_fasta;
    bool       display_confusing_kmers;
    bool       disconnect_confusing_kmers;
    bool       interactive_mode;
    float      dominantNodeThreshold;
    uint32_t   minimal_hitcount_for_confusing_kmer;

    KmerHash         * hashmaps;
    list<KmerNode *> * graphs;
    pthread_mutex_t  * hashmap_m;
    pthread_t        * threads;
    kmer_thread_args * th_args;

    KmerHashTmp      ** hashmapstmp;
    BlockAllocatorST ** blockAllocators;

    // resource needed per worker thread
    SequenceBuffer ** seqbuf;

    int        dotScaling;
    int        dotPerLine;
    int        nDots;
    uint64_t   countReads;
    uint64_t   countReadsCurr;
    uint64_t   countLinks;
    int32_t    countHashmaps;

    void add(Kmer * kmer, Kmer * kmerRC);
    void addParallel(Kmer * kmer, Kmer * kmerRC, int tid);
    void addParallelPrune(Kmer * kmer, Kmer * kmerRC, int tid);
    void add(const char * sequence);
    void processARead(const char * sequence);
    void printAndUpdateContigSizeHistogram(ContigNode * cNode, int tid, bool isNodeRC = false);
    inline uint64_t ceilingByCacheLine(uint64_t in) { return ((in + cacheLineSize - 1)/cacheLineSize)*cacheLineSize; }

  public:
    ofstream ctgout;
    ofstream fastaout;
    uint64_t maxErrorCount;
    uint64_t minRepeatCount;
    uint64_t minValidContigLength;

  friend ostream & operator<<(ostream & out, const KmerCoverageTable & k);
  friend class Graph;
  friend void * linkKmersParallel(void *);
  friend void * populateKmerNodesParallel(void *);
  friend void * processReadsParallel(void *);
  friend void * graphParallel(void *);
  friend void * initializeContigsParallel(void *);
  friend void * initializeContigsParallelTight(void *);
  friend void * pruneHashmap(void *);
};


class kmer_thread_args
{
  public:
    KmerCoverageTable * table;
    int32_t         tid;
    Reader *        reader;
};

#endif  // __KMERHASHTABLE_H__

